"""
Models for a weblog application.

"""


import datetime

from django.conf import settings
from django.db import models
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.contrib.comments import models as comment_models
from django.template.defaultfilters import slugify
from apps.tagging.fields import TagField
from apps.template_utils.markup import formatter

from apps.blog import managers


class Category(models.Model):
    """
    A category that an Entry can belong to.
    
    """
    description = models.TextField(help_text='Use <a href="http://hobix.com/textile/quick.html">Textile</a>.')
    description_html = models.TextField(editable=False, blank=True)
    slug = models.SlugField(editable=False, unique=True,)
    title = models.CharField(maxlength=250)


    class Meta:
        verbose_name_plural = 'Categories'
        ordering = ['title']

    class Admin:
        pass

    def __unicode__(self):
        return self.title

    def save(self):
        self.slug = slugify(self.title)
        self.description_html = formatter(self.description, filter_name='textile')
        super(Category, self).save()
    
    def get_absolute_url(self):
        return ('category_detail', (), { 'slug': self.slug })
    get_absolute_url = models.permalink(get_absolute_url)
    
    def _get_live_entries(self):
        """
        Returns Entries in this Category with status of "live".
        
        Access this through the property ``live_entry_set``.
        
        """
        return self.entry_set.filter(status__exact=1)
    
    live_entry_set = property(_get_live_entries)


class Entry(models.Model):
    """
    An entry in the weblog.
    
    Slightly denormalized, because it uses two fields each for the
    excerpt and the body: one for the actual text the user types in,
    and another to store the HTML version of the Entry (e.g., as
    generated by a text-to-HTML converter like Textile or Markdown).
    This saves having to run the conversion each time the Entry is
    displayed.
    
    Entries can be grouped by categories or by tags or both, or not
    grouped at all.
    
    """
    STATUS_CHOICES = (
        (1, 'Live'),
        (2, 'Draft'),
        (3, 'Hidden'),
    )
    
    author = models.ForeignKey(User)
    body = models.TextField()
    body_html = models.TextField(editable=False, blank=True)
    category = models.ForeignKey(Category, blank=True)
    enable_comments = models.BooleanField(default=True)
    excerpt = models.TextField(blank=True, null=True)
    excerpt_html = models.TextField(blank=True, null=True, editable=False)
    featured = models.BooleanField(default=False)
    published_on = models.DateTimeField('Date posted', default=datetime.datetime.today)
    slug = models.SlugField(editable=False)
    status = models.IntegerField(choices=STATUS_CHOICES, default=1, help_text='Only entries with "live" status will be displayed publicly.')
    tags = TagField()
    title = models.CharField(maxlength=250)
    
    # Managers
    objects = models.Manager()
    live = managers.LiveEntryManager()
    
    class Meta:
        get_latest_by = 'published_on'
        ordering = ['-published_on']
        verbose_name_plural = 'Entries'
    
    class Admin:
        date_hierarchy = 'published_on'
        fields = (
            ('Metadata', { 'fields':
                           ('title', 'published_on', 'author', 'status', 'featured', 'enable_comments') }),
            ('Entry', { 'fields':
                        ('excerpt', 'body') }),
            ('Categorization', { 'fields':
                                 ('tags', 'category') }),
            )
        list_display = ('title', 'published_on', 'author', 'status', 'enable_comments', '_get_comment_count')
        list_filter = ('status',)
        search_fields = ('excerpt', 'body', 'title')

    def __unicode__(self):
        return self.title

    def save(self):
        self.slug = slugify(self.title)
        if self.excerpt:
            self.excerpt_html = formatter(self.excerpt, filter_name='textile')
        self.body_html = formatter(self.body, filter_name='textile')
        super(Entry, self).save()
        
    def get_absolute_url(self):
        return ('entry_detail', (), { 'year': self.published_on.strftime('%Y'),
                                               'month': self.published_on.strftime('%b').lower(),
                                               'day': self.published_on.strftime('%d'),
                                               'slug': self.slug })
    get_absolute_url = models.permalink(get_absolute_url)
    
    def _next_previous_helper(self, direction):
        return getattr(self, 'get_%s_by_published_on' % direction)(status__exact=1)
    
    def get_next(self):
        """
        Returns the next Entry with "live" status by ``published_on``, if
        there is one, or ``None`` if there isn't.
        
        In public-facing views and templates, use this method instead
        of ``get_next_by_published_on``, because ``get_next_by_published_on``
        cannot differentiate live Entries.
        
        """
        return self._next_previous_helper('next')
    
    def get_previous(self):
        """
        Returns the previous Entry with "live" status by ``published_on``,
        if there is one, or ``None`` if there isn't.
        
        In public-facing views and templates, use this method instead
        of ``get_previous_by_published_on``, because
        ``get_previous_by_published_on`` cannot differentiate live Entries.
        
        """
        return self._next_previous_helper('previous')

    def _get_comment_count(self):
        model = settings.USE_FREE_COMMENTS and comment_models.FreeComment or comment_models.Comment
        ctype = ContentType.objects.get_for_model(self)
        return model.objects.filter(content_type__pk=ctype.id, object_id__exact=self.id).count()
    _get_comment_count.short_description = 'Number of comments'